/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QTextStream>

#include "ForgettingCurvePlot.h"
#include "Logger.h"

ForgettingCurvePlot::ForgettingCurvePlot(QDialog* parent)
    : QDialog(parent),
    mForgettingCurveSampleCrv(tr("Experimental data")),
    mForgettingCurveExpCrv(tr("Fitted exponential curve")),
    mAutoReplot(false),
    mIntvMin(-1),
    mIntvMax(-1)
{
	ui.setupUi(this);

	QObject::connect(ui.testCountComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(testCountChanged(const QString &)));
	QObject::connect(ui.closeButton, SIGNAL(clicked()), this, SLOT(accept()));

	QGridLayout* sampleDistributionGrid = new QGridLayout(ui.sampleDistributionPlotArea);
	sampleDistributionGrid->setContentsMargins(0, 0, 0, 0);
	sampleDistributionGrid->addWidget(&mSampleDistributionPlot);
	QwtPlotGrid* sampleDistributionPlotGrid = new QwtPlotGrid;
	sampleDistributionPlotGrid->enableXMin(true);
	sampleDistributionPlotGrid->enableYMin(true);
	sampleDistributionPlotGrid->setMajPen(QPen(Qt::black, 0, Qt::DotLine));
	sampleDistributionPlotGrid->setMinPen(QPen(Qt::gray, 0 , Qt::DotLine));
	sampleDistributionPlotGrid->attach(&mSampleDistributionPlot);
	mSampleDistributionPlot.setCanvasBackground(QColor(Qt::white));
	mSampleDistributionPlot.setAxisAutoScale(QwtPlot::yLeft);
	mSampleDistributionPlot.setAutoReplot(true);
	mSampleDistributionPlot.setAxisTitle(QwtPlot::yLeft, tr("Sample size"));
	mSampleDistributionPlot.setAxisTitle(QwtPlot::xBottom, tr("Interval (day)"));
	mSampleDistributionHistogram = new HistogramItem();
	mSampleDistributionHistogram->setColor(Qt::darkCyan);
	mSampleDistributionHistogram->attach(&mSampleDistributionPlot);

	QGridLayout* forgettingCurveGrid = new QGridLayout(ui.forgettingCurvePlotArea);
	forgettingCurveGrid->setContentsMargins(0, 0, 0, 0);
	forgettingCurveGrid->addWidget(&mForgettingCurvePlot);
	QwtPlotGrid* forgettingCurvePlotGrid = new QwtPlotGrid;
	forgettingCurvePlotGrid->enableXMin(true);
	forgettingCurvePlotGrid->enableYMin(true);
	forgettingCurvePlotGrid->setMajPen(QPen(Qt::black, 0, Qt::DotLine));
	forgettingCurvePlotGrid->setMinPen(QPen(Qt::gray, 0 , Qt::DotLine));
	forgettingCurvePlotGrid->attach(&mForgettingCurvePlot);
	mForgettingCurveSampleCrv.attach(&mForgettingCurvePlot);
	mForgettingCurveExpCrv.attach(&mForgettingCurvePlot);
	mForgettingCurvePlot.setAxisAutoScale(QwtPlot::yLeft);
	mForgettingCurvePlot.setAutoReplot(true);
	mForgettingCurvePlot.setCanvasBackground(QColor(Qt::white));
	mForgettingCurvePlot.setAxisTitle(QwtPlot::yLeft, tr("Passing test percentile (%)"));
	mForgettingCurvePlot.setAxisTitle(QwtPlot::xBottom, tr("Interval (day)"));
	mForgettingCurvePlot.insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
}

ForgettingCurvePlot::~ForgettingCurvePlot()
{

}

int ForgettingCurvePlot::exec()
{
	int width = this->geometry().width();
	//LOG<<"width="<<width<<endl;
	float vSpliterSizeRate = 0.7f;
	QList<int> vSpliterSize;
	vSpliterSize<<vSpliterSizeRate * width<<(1.0f-vSpliterSizeRate) * width;
	//LOG<<vSpliterSizeRate * width<<"\t"<<(1.0f-vSpliterSizeRate) * width<<endl;
	ui.vsplitter->setSizes(vSpliterSize);

	this->setMinimumHeight(this->geometry().height());

	return QDialog::exec();
}

void ForgettingCurvePlot::setData(map<int, map<int, TestResultCounter> >& testResultCounterMap, map<int , int>& testBestIntvMap)
{
	ui.testCountComboBox->clear();
	mTestCountList.clear();
	mSampleDistributionMap.clear();
	mRetentionMap.clear();
	mTestBestIntv = testBestIntvMap;
	for (	map<int, map<int, TestResultCounter> >::iterator test_count_it = testResultCounterMap.begin();
			test_count_it != testResultCounterMap.end();
			test_count_it++ )
	{
		int testCount = test_count_it->first;
		QString testCountStr = QString("%1").arg(testCount);
		if (!mTestCountList.contains(testCountStr))
		{
			mTestCountList<<testCountStr;
			ui.testCountComboBox->addItem(testCountStr);
		}

		map<int, TestResultCounter>& intvMap = test_count_it->second;
		for (	map<int, TestResultCounter>::iterator intv_it = intvMap.begin();
				intv_it != intvMap.end();
				intv_it++ )
		{
			int interval = intv_it->first;
			TestResultCounter& counter = intv_it->second;
			mSampleDistributionMap[testCount][interval] = counter.sampleCount();
			mSamplePassMap[testCount][interval] = counter.passSampleCount;
			mSampleFailMap[testCount][interval] = counter.failSampleCount;
			if (counter.isValid)
				mRetentionMap[testCount][interval] = counter.passRate();
		}
	}

	if (ui.testCountComboBox->count() != 0)
	{
		mAutoReplot = true;
		ui.testCountComboBox->setCurrentIndex(0);
		testCountChanged(ui.testCountComboBox->currentText());
		mAutoReplot = false;
	}
}

void ForgettingCurvePlot::testCountChanged(const QString& text)
{
	int testCount = text.toInt();
	if ((mAutoReplot || this->isVisible()) && testCount > 0)
	{
		drawSampleDistribution(testCount);
		drawForgettingCurve(testCount);
		setStatistics(testCount);
		if (mIntvMin != -1 && mIntvMax != -1)
		{
			mSampleDistributionPlot.setAxisScale(QwtPlot::xBottom, mIntvMin, mIntvMax);
			mForgettingCurvePlot.setAxisScale(QwtPlot::xBottom, mIntvMin, mIntvMax);
		}
	}
}

void ForgettingCurvePlot::drawSampleDistribution(int testCount)
{
	map<int, map<int, int> >::iterator sam_it = mSampleDistributionMap.find(testCount);
	if (sam_it == mSampleDistributionMap.end())
		return;

	int totalCount = 0;
	mIntvMax = -9999999;
	float width = 0.8f;
	QwtArray<QwtDoubleInterval> intvArray;
	QwtArray<double> countArray;
	map<int, int>& distMap = sam_it->second;
	for (	map<int, int>::iterator count_it = distMap.begin();
			count_it != distMap.end();
			count_it++)
	{
		int intv = count_it->first;
		if (intv > mIntvMax) mIntvMax = intv;
		intvArray.push_back(QwtDoubleInterval(intv-width/2, intv+width/2));
		int count = count_it->second;
		totalCount += count;
		countArray.push_back(count);
	}

	mSampleDistributionHistogram->setData(QwtIntervalData(intvArray, countArray));

	QString title = tr("Sample Distribution (sample size = %1 )").arg(totalCount);
	mSampleDistributionPlot.setTitle(title);
}

void ForgettingCurvePlot::drawForgettingCurve(int testCount)
{
	map<int, map<int, float> >::iterator ret_it = mRetentionMap.find(testCount);
	if (ret_it == mRetentionMap.end())
		return;

	QwtArray<double> intvArray;
	QwtArray<double> retentionArray;
	map<int, float>& intvRetMap = ret_it->second;
	for (	map<int, float>::iterator intv_ret_it = intvRetMap.begin();
			intv_ret_it != intvRetMap.end();
			intv_ret_it++)
	{
		int intv = intv_ret_it->first;
		intvArray.push_back(intv);
		float retention = intv_ret_it->second;
		retentionArray.push_back(retention);
	}

	QwtSymbol sym;
	sym.setStyle(QwtSymbol::Ellipse);
	sym.setPen(QColor(Qt::black));
	sym.setSize(10);

	mForgettingCurveSampleCrv.setSymbol(sym);
	mForgettingCurveSampleCrv.setStyle(QwtPlotCurve::NoCurve);
	mForgettingCurveSampleCrv.setData(intvArray, retentionArray);

	map<int , int>::iterator exp_it = mTestBestIntv.find(testCount);
	if (exp_it == mTestBestIntv.end())
		return;

	int bestIntvDay = exp_it->second;
	QwtArray<double> intvExpArray;
	QwtArray<double> retExpArray;
	float div = 0.1f;
	int intvMax = mIntvMax;
	for (int i=0; i<= intvMax/div; i++)
	{
		float intvExp = div * float(i);
		intvExpArray.push_back(intvExp);
		float retExp = exp(-1.0f/float(bestIntvDay) * intvExp);
		retExpArray.push_back(retExp);
	}

	mForgettingCurveExpCrv.setData(intvExpArray, retExpArray);
	QString title = tr("Exponential Forgetting Curve");
	title.append(QString(" (R=exp(-t/s), s=%1)").arg(bestIntvDay));
	mForgettingCurvePlot.setTitle(title);
}

void ForgettingCurvePlot::setStatistics(int testCount)
{
	map<int, map<int, int> >::iterator p_it = mSamplePassMap.find(testCount);
	if (p_it == mSamplePassMap.end())
		return;
	map<int, int>& spMap = p_it->second;

	QString dataTableStr;
	QTextStream ds(&dataTableStr);
	ds.setRealNumberPrecision(2);
	ds<<"<table border=\"0.5\">";
	ds<<"<tr><th>Interval</th><th>Fail</th><th>Pass</th><th>%</th></tr>";
	for (	map<int, int>::iterator it = spMap.begin();
			it != spMap.end();
			it ++)
	{
		ds<<"<tr>";
		int intv = it->first;
		int passCount = it->second;
		int failCount = mSampleFailMap[testCount][intv];
		ds<<"<th>"<<intv<<"</th>";
		ds<<"<th>"<<failCount<<"</th>";
		ds<<"<th>"<<passCount<<"</th>";
		ds<<"<th>"<<float(passCount)/float(passCount+failCount)<<"</th>";
		ds<<"</tr>";
	}
	ds<<"</table>";

	ui.forgettingInfoBrowser->clear();
	ui.forgettingInfoBrowser->insertHtml(dataTableStr);
}

